#pragma once
#include "hzpch.h"
#include "../core.h"


namespace Hazel{

    // Events in Hazel are currently blocking, meaning when an event occurs it
    // immediately get dispatched and must be dealt with right then an there.
    // For the future, a better strategy might be to buffer enevts in an event
    // bus and process them during the "event" part of the update stage.
    
    enum class EventType
    {
        None =0,
        WindowClose,WindowResize,WindowFocus,WindowLostFocus,Windowmoved,
        AppTick,AppUpdate,AppRander,
        KeyPressed,KeyReleased,KeyTyped,
        MouseButtonPressed,MouseButtonReleased,MouseMoved,MouseScrolled  
    };

    enum class EventCateGory
    {
        None=0,
        EventCategoryApplication    =   BIT(0),
        EventCategoryInput          =   BIT(1),
        EventCategoryKeyboard       =   BIT(2),
        EventCategoryMouse          =   BIT(3),
        EventCategoryMouseButton    =   BIT(4)
        // EventCategoryApplication    =   BIT(5),
    };

#define EVENT_CLASS_TYPE(type) static EventType GetStaticType(){return EventType::##type;}\
                                virtual EventType GetEventType() const override {return GetStaticType();}\
                                virtual const char* GetName() const override{return #type;}

#define EVENT_CLASS_CATEGORY(category) virtual int GetCateGoryFlags() const override { return (int)category;}

    // class HAZEL_API Event
    class HAZEL_API Event
    {
        friend class EventDispatcher;
    public:
        virtual EventType GetEventType() const =0;
        virtual const char* GetName() const =0;
        virtual int GetCateGoryFlags() const =0;
        virtual std::string ToString() const {return GetName();}

        inline bool IsInCategory(EventCateGory category)
        {
            return GetCateGoryFlags() & (int)category;
        }

        inline bool IsHandled() const {return m_Handled;}
    protected:
        bool m_Handled = false;
    };

    class  EventDispatcher
    {
        template<typename T>
        using EventFn= std::function<bool(T&)>;
    public:
        EventDispatcher(Event& event)
            :m_Event(event)
        {
        }

        template<typename T>
        bool Dispatch(EventFn<T> func)
        {
            if(m_Event.GetEventType()==T::GetStaticType())
            {
                m_Event.m_Handled = func(*(T*)&m_Event);
                return true;
            }
            return false;
        }
    private:
        Event& m_Event;
    };

    inline std::ostream& operator <<(std::ostream& os,const Event& e)
    {
        return os<<e.ToString();
    }
}